/*
 * @title instructions.c
 * @brief Instructions
 * @author Nathalie Kauv & Laura Martellotto
 *
 * @date 27 déc. 2011
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "instructions.h"
#include "interfaceMenu.h"

/**
 * @brief Permet d'initialiser le tableau de mot appelé programme
 *
 * @param codInst est le mot qui se rajoutera dans programme lorsqu'il sera initialisé
 * @param rang est le rang à partir du quel la chaine de caractère inst n'a pas été
 *          parcouru
 * @param inst est la chaine de caractère lu dans le fichier
 *
 * Parcours le mot pour obtenir le codage de la source lorsque le mode
 * d'opération est Immédiat ou Direct. Ensuite, il enregistre le mot
 * dans le tableau de mot programme.
 */
void parSourImmDir(mot codInst, int rang, char * inst) {
    ++rang;
    codInst.codage.source = 0;
    int j= 0;
    char strSource[6];
    for(j=0; inst[rang] != '\0'; ++j) {
        strSource[j] = inst[rang];
        ++rang;
    }
    char * resteChaine;
    unsigned source = strtol(strSource, &resteChaine, 10);
    mot deuxmot;
    deuxmot.brut = source;
    unsigned lastele = instCourant;
    programme[lastele] = codInst;
    ++instCourant;
    programme[lastele+1] = deuxmot;
    ++instCourant;
}

/**
 * @brief Permet d'initialiser le tableau de mot appelé programme
 *
 * @param codInst est le mot qui se rajoutera dans programme lorsqu'il sera initialisé
 * @param rang est le rang à partir du quel la chaine de caractère inst n'a pas été
 *          parcouru
 * @param inst est la chaine de caractère lu dans le fichier
 *
 * Parcours le mot pour obtenir le codage de la source lorsque le mode
 * d'opération est Registre ou Indirect. Ensuite, il enregistre le mot
 * dans le tableau de mot programme.
 */
void parSourRegInd(mot codInst, int rang, char * inst) {
    unsigned source = inst[rang+1];
    if (source>= 48 && source <=55) {
        codInst.codage.source = source;
    }
    programme[instCourant] = codInst;
    ++instCourant;
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est load.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage pour la
 * destination est un registre, et la source est direct, indirect ou immédiat.
 * Elle initialise ainsi le mode opératoire et le code du mot. Elle peut
 * initialiser un deuxième mot lorsque le mode d'opératoire est immédiat ou direct.
 * Elle peut même initialiser un troisième lorsqu'il est immédiat et direct.
 */
void traitLoad (char * inst)
{
    mot codInst;
    codInst.codage.codeop = LOAD;
    int i = 0;
    while (toupper(inst[i]) != 'R' ) {
        ++i;
    }
    ++i;
    unsigned dest = inst[i];
    if (dest>= 48 && dest <=55) {
        codInst.codage.dest = dest;
    }
    i = i + 2;
    if (inst[i] == '#') {
        codInst.codage.mode = REGIMM;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) != 'R') {
        codInst.codage.mode = REGDIR;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) == 'R') {
        codInst.codage.mode = REGIND;
        ++i;
        parSourRegInd(codInst, i, inst);
    }
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est store.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage pour la
 * destination est un registre, et la source est direct, indirect ou immédiat.
 * Elle initialise ainsi le mode opératoire et le code du mot. Elle peut
 * initialiser un deuxième mot lorsque le mode d'opératoire est immédiat ou direct.
 * Elle peut même initialiser un troisième lorsqu'il est immédiat et direct.
 */
void traitStore (char * inst)
{
    mot codInst;
    codInst.codage.codeop = STORE;
    int i = 0;
    while (inst[i] != '[' ) {
        ++i;
    }
    ++i;
    if (toupper(inst[i]) == 'R') {
        ++i;
        unsigned dest = inst[i];
        if (dest>= 48 && dest <=55) {
            codInst.codage.dest = dest;
        }
        while (inst[i] != ',') {
            ++i;
        }
        ++i;
        if (inst[i] == '#') {
            codInst.codage.mode = INDIMM;
            parSourImmDir(codInst, i, inst);
        }
        else if (toupper(inst[i]) == 'R') {
            codInst.codage.mode = INDREG;
            parSourRegInd(codInst, i, inst);
        }
    }
    else {
        int j= 0;
        char strDest[6];
        for(j=0; inst[i] != ']'; ++j) {
            strDest[j] = inst[i];
            ++i;
        }
        char * resteChaine;
        unsigned dest = strtol(strDest, &resteChaine, 10);
        codInst.codage.dest = 0;
        while (inst[i] != ',') {
              ++i;
        }
        ++i;
        if (inst[i] == '#') {
            codInst.codage.mode = DIRIMM;
            parSourImmDir(codInst, i, inst);
        }
        else if (toupper(inst[i]) == 'R') {
            codInst.codage.mode = DIRREG;
            parSourRegInd(codInst, i, inst);
        }
        mot troismot;
        troismot.brut = dest;
        programme[instCourant] = troismot;
        ++instCourant;
    }
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est add.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage est pour la
 * destination, un registre, et la source peut être registre, direct, indirect ou
 * immédiat. Elle initialise ainsi le mode opératoire et le code du mot.
 */
void traitAdd (char * inst)
{
    mot codInst;
    codInst.codage.codeop = ADD;
    int i = 0;
    while (inst[i] != 'R' ) {
            ++i;
    }
    ++i;
    unsigned dest = inst[i];
    if (dest>= 48 && dest <=55) {
        codInst.codage.dest = dest;
    }
    i=i+2;
    if (toupper(inst[i]) == 'R') {
        codInst.codage.mode = REGREG;
        parSourRegInd(codInst, i, inst);
    }
    else if (inst[i] == '#') {
        codInst.codage.mode = REGIMM;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) != 'R') {
        codInst.codage.mode = REGDIR;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) == 'R') {
        ++i;
        codInst.codage.mode = REGIND;
        parSourRegInd(codInst, i, inst);
    }
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est sub.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage est pour la
 * destination, un registre, et la source peut être registre, direct, indirect ou
 * immédiat. Elle initialise ainsi le mode opératoire et le code du mot.
 */
void traitSub (char * inst)
{
    mot codInst;
    codInst.codage.codeop = SUB;
    int i = 0;
    while (inst[i] != 'R' ) {
            ++i;
    }
    ++i;
    unsigned dest = inst[i];
    if (dest>= 48 && dest <=55) {
        codInst.codage.dest = dest;
    }
    i=i+2;
    if (toupper(inst[i]) == 'R') {
        codInst.codage.mode = REGREG;
        parSourRegInd(codInst, i, inst);
    }
    else if (inst[i] == '#') {
        codInst.codage.mode = REGIMM;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) != 'R') {
        codInst.codage.mode = REGDIR;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) == 'R') {
        ++i;
        codInst.codage.mode = REGIND;
        parSourRegInd(codInst, i, inst);
    }
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est jmp.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage la
 * source est immédiat. Elle initialise ainsi le mode opératoire et le code du mot.
 * Elle peut initialiser un deuxième mot lorsque le mode d'opératoire est
 * immédiat
 */
void traitJmp (char * inst)
{
    mot codInst;
    codInst.codage.codeop = JMP;
    codInst.codage.mode = 0;
    codInst.codage.dest = 0;
    int i=0;
    while(inst[i] != '#') {
        ++i;
    }
    parSourImmDir(codInst, i, inst);
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est jeq.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage la
 * source est immédiat. Elle initialise ainsi le mode opératoire et le code du mot.
 * Elle peut initialiser un deuxième mot lorsque le mode d'opératoire est
 * immédiat
 */
void traitJeq (char * inst)
{
    mot codInst;
    codInst.codage.codeop = JEQ;
    codInst.codage.mode = 0;
    codInst.codage.dest = 0;
    int i=0;
    while(inst[i] != '#') {
        ++i;
    }
    parSourImmDir(codInst, i, inst);
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est call.
 * Elle parcourt inst pour initisialiser le mot. Le mode d'adressage la
 * source est immédiat. Elle initialise ainsi le mode opératoire et le code du mot.
 * Elle peut initialiser un deuxième mot lorsque le mode d'opératoire est
 * immédiat.
 */
void traitCall (char * inst)
{
    mot codInst;
    codInst.codage.codeop = CALL;
    codInst.codage.mode = 0;
    codInst.codage.dest = 0;
    int i=0;
    while(inst[i] != '#') {
        ++i;
    }
    parSourImmDir(codInst, i, inst);
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est jeq.
 * Elle parcourt inst pour initisialiser le mot. Mais, elle n'a pas de mode
 * d'adressage. Elle initialise aucun mode opératoire et code du mot.
 */
void traitRet (char * inst)
{
    mot codInst;
    codInst.codage.codeop = RET;
    codInst.codage.mode = 0;
    codInst.codage.dest = 0;
    codInst.codage.source = 0;
    programme[instCourant] = codInst;
    ++instCourant;
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est push.
 * Elle parcourt inst pour initisialiser le mot. Mais, elle le mode
 * d'adressage, en source peut être soit registre, immédiat, direct ou
 * indirect. Elle initialise ainsi le mode opératoire et le code du mot.
 * Elle peut initialiserun deuxième mot lorsque le mode d'opératoire est
 * direct.
 */
void traitPush (char * inst)
{
    mot codInst;
    codInst.codage.codeop = PUSH;
    int i=0;
    if (toupper(inst[i]) == 'R') {
        codInst.codage.mode = REGREG;
        parSourRegInd(codInst, i, inst);
    }
    else if (inst[i] == '#') {
        codInst.codage.mode = INDIMM;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) != 'R') {
        codInst.codage.mode = REGDIR;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) == 'R') {
        codInst.codage.mode = REGIND;
        ++i;
        parSourRegInd(codInst, i, inst);
    }

}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est pop.
 * Elle parcourt inst pour initisialiser le mot. Mais, elle le mode
 * d'adressage, en destination peut être soit registre, direct ou
 * indirect. Elle initialise ainsi le mode opératoire et le code du mot.
 * Elle peut initialiserun deuxième mot lorsque le mode d'opératoire est
 * direct.
 */
void traitPop (char * inst)
{
    mot codInst;
    codInst.codage.codeop = POP;
    int i=0;
    if (toupper(inst[i]) == 'R') {
        codInst.codage.mode = REGIMM;
        parSourRegInd(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) != 'R') {
        codInst.codage.mode = DIRREG;
        parSourImmDir(codInst, i, inst);
    }
    else if (inst[i] == '[' && toupper(inst[i+1]) == 'R') {
        codInst.codage.mode = INDREG;
        ++i;
        parSourRegInd(codInst, i, inst);
    }

}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Fait le traitement lorsque l'instruction lue dans le fichier est halt,
 * pour arreter le programme. Elle parcourt inst pour initisialiser le mot.
 * Mais, le mode d'adressage, le code et le mode opératoire ne sont pas
 * initialisé.
 */
void traitHalt (char * inst)
{
    mot codInst;
    codInst.codage.codeop = HALT;
    codInst.codage.mode = 0;
    codInst.codage.dest = 0;
    codInst.codage.source = 0;
    programme[instCourant] = codInst;
    ++instCourant;
}

/**
 * @brief Initialise le codage du mot
 *
 * @param inst la chaine de caractère lue dans le fichier
 *
 * Essaye de transformer la chaine lue dans le fichier en mot, en appelant les
 * fonctions trait*. Elle permet de reconnaitre les instructions.
 */
void transMot (char * inst)
{
    int i=0;
    char operateur[5];
    for(i=0; (inst[i] != '\0') && (inst[i] != ' '); ++i)
        operateur[i] = tolower(inst[i]);
    operateur[i]='\0';
    char newStr[strlen(inst)];
    unsigned j;
    unsigned v = 0;
    for (j = i; j < strlen(inst); ++j)
        if (inst[j] != ' ') {
            newStr[v] = inst[j];
            ++v;
        }
    if (strcmp(operateur, "load") == 0)
        traitLoad(newStr);
    else if (strcmp(operateur, "store") == 0)
        traitStore(newStr);
    else if (strcmp(operateur, "add") == 0)
        traitAdd(newStr);
    else if (strcmp(operateur, "sub") == 0)
        traitSub(newStr);
    else if (strcmp(operateur, "jmp") == 0)
        traitJmp(newStr);
    else if (strcmp(operateur, "jeq") == 0)
        traitJeq(newStr);
    else if (strcmp(operateur, "call") == 0)
        traitCall(newStr);
    else if (strcmp(operateur, "ret") == 0)
        traitRet(newStr);
    else if (strcmp(operateur, "push") == 0)
        traitPush(newStr);
    else if (strcmp(operateur, "pop") == 0)
        traitPop(newStr);
    else if (strcmp(operateur, "halt") == 0)
        traitHalt(newStr);
}

/**
 * @brief Lit le fichier pour initialiser un mot stocké dans programme, un tableau.
 *
 * Lit le fichier fix.txt, où toutes les instructions de procsi attendent
 * d'être exécuté. Il lit ligne par ligne, jusqu'à qu'il en trouve plus.
 * Il ferme le fichier et lance la fonction lancerProgramme(), qui lira sur le
 * tableau pour obtenir le résultat des instructions.
 */
void Read ()
{
    FILE *fichier;
    char *ligne;
    long taille_file;
    fichier = fopen("../fic.txt", "r");
    fseek(fichier,SEEK_END,0);
    taille_file=ftell(fichier);
    fseek(fichier,SEEK_SET,0);
    ligne=(char *)malloc(sizeof(char) * taille_file);
    instCourant = 0;
    char * notEOF = fgets(ligne,25,fichier);
    while(notEOF) {
        transMot (ligne);
        notEOF = fgets(ligne,25,fichier);
    }
    free(ligne);
    fclose(fichier);
    lancerProgramme(programme);
}

/**
 * @brief Ecrit sur le fichier fic.txt les instructions à exécuter
 *
 * Cette fonction permet d'écrire dans le fichier fic.txt les instructions que
 * l'on souhaite exécuter.
 */
void Write() {
    FILE* fichier = NULL;
    fichier = fopen("../fic.txt", "w");
    if (fichier != NULL)
    {
        fputs("Add R2, #2\n", fichier);
        fputs("Add R1, #3\n", fichier);
        fputs("Add R1, R2\n", fichier);
        fputs("Add R3, [R2]\n", fichier);
        fputs("Add R1, [3]\n", fichier);
        fputs("Sub R1, #3\n", fichier);
        fputs("Sub R1, R2\n", fichier);
        fputs("Sub R3, [R2]\n", fichier);
        fputs("Sub R1, [3]\n", fichier);
        fputs("Load R1, [3]\n", fichier);
        fputs("Load R2, [R3]\n", fichier);
        fputs("Load R1, [13]\n", fichier);
        fputs("Store [R2],  #23\n", fichier);
        fputs("Store [R4], R4\n", fichier);
        fputs("Store [4], #54\n", fichier);
        fputs("Store [5], R2\n", fichier);
        fputs("Jmp #5\n", fichier);
        fclose(fichier);
    }
}
